#include <stdio.h>
#include <string.h>

#include "xparameters.h"
#include "xuartps.h"
#include "xscugic.h"

#include "sysinc.h"
#include "sys_clock.h"
#include "uart.h"

// UART 接收到数据中断回调
typedef void (*irq_callback_uart_on_rx_byte_t)(std_uart_t *uart, unsigned char ch);
// UART 发送完成中断回调
typedef void (*irq_callback_uart_on_tx_empty_t)(std_uart_t *uart);

typedef struct _std_uart {
	int device_id;		// 串口设备ID
	int intc_device_id;	// 中断控制器设备ID
	int int_id;			// 串口中断ID
	XUartPs dev;    	// 串口驱动程序实例
#if !USE_FREERTOS
	XScuGic intc;      // 中断控制器驱动程序实例
#endif
	irq_callback_uart_on_rx_byte_t on_rx_byte;
	irq_callback_uart_on_tx_empty_t on_tx_empty;
} std_uart_t;

#ifdef XPAR_XUARTPS_0_DEVICE_ID
static std_uart_t _ps_uart0 = {XPAR_XUARTPS_0_DEVICE_ID, XPAR_SCUGIC_SINGLE_DEVICE_ID, XPAR_XUARTPS_0_INTR};
std_uart_t* PS_UART0 = &_ps_uart0;
#endif

#ifdef XPAR_XUARTPS_1_DEVICE_ID
static std_uart_t _ps_uart1 = {XPAR_XUARTPS_1_DEVICE_ID, XPAR_SCUGIC_SINGLE_DEVICE_ID, XPAR_XUARTPS_1_INTR};
std_uart_t* PS_UART1 = &_ps_uart1;
#endif


// UART中断处理函数
static void _uart_intr_handler(void *data) {
	std_uart_t *uart = (std_uart_t*)data;

    // 读取中断ID寄存器，判断触发的是哪种中断
    uint32_t isr_status;
    isr_status = XUartPs_ReadReg(uart->dev.Config.BaseAddress, XUARTPS_IMR_OFFSET);
    isr_status &= XUartPs_ReadReg(uart->dev.Config.BaseAddress, XUARTPS_ISR_OFFSET);

    // 判断中断标志位 RxFIFO 是否触发
    if (isr_status & (uint32_t)XUARTPS_IXR_RXOVR) {
    	// 读取数据
        uint8_t ch = XUartPs_RecvByte(uart->dev.Config.BaseAddress);
        // 回调
        if(uart->on_rx_byte) uart->on_rx_byte(uart, ch);
        // 清除中断标志
        XUartPs_WriteReg(uart->dev.Config.BaseAddress, XUARTPS_ISR_OFFSET, XUARTPS_IXR_RXOVR) ;
    }
}

std_err_t uart_init(std_uart_t *uart, int baudrate, irq_callback_uart_on_rx_byte_t on_rx_byte, irq_callback_uart_on_tx_empty_t on_tx_empty) {
	s32 status;

	// 查找串口对应的硬件配置信息
    XUartPs_Config *uart_cfg = XUartPs_LookupConfig(uart->device_id);
    if(NULL == uart_cfg) return STD_ERR_FAIL;
    // 初始化串口驱动程序实例
    status = XUartPs_CfgInitialize(&uart->dev, uart_cfg, uart_cfg->BaseAddress);
    if(status != XST_SUCCESS) return STD_ERR_FAIL;

    // UART设备自检
    //status = XUartPs_SelfTest(&uart->dev);
    //if(status != XST_SUCCESS) return STD_ERR_FAIL;

    // 设置工作模式: 正常模式
    XUartPs_SetOperMode(&uart->dev, XUARTPS_OPER_MODE_NORMAL);
    // 设置波特率
    status = XUartPs_SetBaudRate(&uart->dev, baudrate);
    if(status != XST_SUCCESS) return STD_ERR_FAIL;

    if(on_rx_byte || on_tx_empty) {
		// 设置UART的中断触发方式
		uint32_t int_mask = 0;
		if(on_rx_byte) {
			// 设置 RxFIFO 的中断触发等级
			XUartPs_SetFifoThreshold(&uart->dev, 1);
			int_mask |= XUARTPS_IXR_RXOVR;
		}
		if(on_tx_empty) {
			int_mask |= XUARTPS_IXR_TXEMPTY;
		}
		XUartPs_SetInterruptMask(&uart->dev, int_mask);

		uart->on_rx_byte = on_rx_byte;
		uart->on_tx_empty = on_tx_empty;

#if !USE_FREERTOS
		// 为中断设置中断处理函数
		status = XScuGic_Connect(&uart->intc, uart->int_id, (Xil_ExceptionHandler)_uart_intr_handler, (void*)uart);
		if (status != XST_SUCCESS) return STD_ERR_FAIL;

		// 使能 GIC 中的串口中断
		XScuGic_Enable(&uart->intc, uart->int_id);
#else
		// 为中断设置中断处理函数
		status = xPortInstallInterruptHandler(uart->int_id, (Xil_ExceptionHandler)_uart_intr_handler, (void*)uart);
		if (status != pdPASS) return STD_ERR_FAIL;
		// 使能 GIC 中的串口中断
		vPortEnableInterrupt(uart->int_id);
#endif
    }

    return STD_ERR_OK;
}

static rx_ret_t uart_read(std_uart_t *uart, unsigned char *buf, size_t size, size_t* plen, long timeout, long interval, const char *okFlag, rx_flag_t okFlagType, const char *failFlag, rx_flag_t failFlagType) {
	if(!uart || !buf || !size) return STD_ERR_INVALID_ARG;
	char* data = (char*)buf;
	rx_ret_t ret = RX_RET_ERROR;
	size_t len = 0;
	uint32_t uartBaseAddress = uart->dev.Config.BaseAddress;

	// 清空缓冲区，以免因残留数据导致数据标志匹配错误
	if(okFlag || failFlag) memset(buf, 0, size);

	// 同步接收数据时，应关闭接收中断，防止数据被中断服务程序读走了
	//uart_rx_irq_enable(dev, false);

	uint32_t start = osGetMs();
	uint32_t lastByteTime = 0;

	while(1) {
		if(XUartPs_IsReceiveData(uartBaseAddress)) {
			// 收到数据
			data[len++] = XUartPs_ReadReg(uartBaseAddress, XUARTPS_FIFO_OFFSET);
			lastByteTime = osGetMs();

			if(len >= size) {
				// 因缓冲区满中止
				ret = RX_RET_FULL;
				break;
			}

			if(okFlag && okFlagType != RX_FLAG_NULL) {
				if( (okFlagType == RX_FLAG_STRING && strstr(data, okFlag)) ||
				    (okFlagType >= RX_FLAG_BINARY && memmem(data, len, okFlag, okFlagType)) )
				{
					// 因 OK 标志中止
					ret = RX_RET_OK;
					break;
				}
			}
			if(failFlag && failFlagType != RX_FLAG_NULL) {
				if( (failFlagType == RX_FLAG_STRING && strstr(data, failFlag)) ||
				    (failFlagType >= RX_FLAG_BINARY && memmem(data, len, failFlag, failFlagType)) )
				{
					// 因 Failed 标志中止
					ret = RX_RET_FAIL;
					break;
				}
			}
		}

		if(timeout > 0 && (osElapsedMs(start) >= timeout)) {
			// 因总超时中止
			ret = RX_RET_TIMEOUT;
			break;
		}
		if(interval > 0 && lastByteTime && (osElapsedMs(lastByteTime) >= interval)) {
			// 因间隔超时中止
			ret = RX_RET_INTERVAL;
			break;
		}

		// !!! 注意：这里一定要用 vTaskDelay() 进行延时，以便 FreeRTOS 有机会进行任务调度 !!!
		// 文档中说 “A delay time of zero just forces a reschedule”，但实验证明 vTaskDelay(0) 并不会让当前任务轮出！
		// TODO 所以还是要通过 vTaskDelay(portTICK_PERIOD_MS) 休眠一个时间片，强制当前任务轮出，以让其它任务得以执行！
		vTaskDelay(0);
	}

	if(plen) *plen = len;
    return ret;
}

static inline uint8_t uart_read_byte(std_uart_t *uart) {
	// Wait until there is data
	// !!! 注意：这里一定要用 vTaskDelay() 进行延时，以便 FreeRTOS 有机会进行任务调度 !!!
	// 文档中说 “A delay time of zero just forces a reschedule”，但实验证明 vTaskDelay(0) 并不会让当前任务轮出！
	// TODO 所以还是要通过 vTaskDelay(portTICK_PERIOD_MS) 休眠一个时间片，强制当前任务轮出，以让其它任务得以执行！
	while(!XUartPs_IsReceiveData(uart->dev.Config.BaseAddress)) vTaskDelay(0);
	// read and return byte
	return (uint8_t) XUartPs_ReadReg(uart->dev.Config.BaseAddress, XUARTPS_FIFO_OFFSET);
}

static std_err_t uart_readline(std_uart_t *uart, unsigned char *buf, size_t size, size_t* len) {
	if(!uart || !buf || size <= 2) return STD_ERR_INVALID_ARG;
	// 数据后面的两个字节填写 '\0'
	size = size - 2;

	int nbytes = 0;
	for(; nbytes < size; buf++, nbytes++) {
		// 当使用 FreeRTOS 时不能用 XUartPs_RecvByte() 了，因为它是死阻塞
		//*buf = XUartPs_RecvByte(uart->dev.Config.BaseAddress);
		// 等待上位机发数据过来
		// !!! 注意：任务将在此阻塞 !!!
		*buf = uart_read_byte(uart);
		if(*buf == '\n') {
			// 数据后面的两个字节填写 '\0'
			buf[1] = '\0'; buf[2] = '\0';
			if(len) *len = nbytes + 1;
			return STD_ERR_OK;
		}
	}

	// 缓冲区满了，但还未遇到行结束
	// 数据后面的两个字节填写 '\0'
	buf[0] = '\0'; buf[1] = '\0';
	if(len) *len = size;
	return STD_ERR_OK;
}

static ssize_t uart_write(std_uart_t *uart, const unsigned char *buf, size_t nbytes, size_t* len) {
	if(!uart || !buf || !nbytes) return STD_ERR_INVALID_ARG;

	//size_t len2 = XUartPs_Send(&uart->dev, (uint8_t*)buf, nbytes);
	//if(len) *len = len2;

	for(int i = 0; i < nbytes; buf++, i++) {
		//if(*buf == '\n') outbyte('\r');
		//outbyte(*buf);
		XUartPs_SendByte(uart->dev.Config.BaseAddress, *buf);
	}
	if(len) *len = nbytes;

    return STD_ERR_OK;
}


//
// 以下是针对标准收发器 transceiver 接口的实现
//
static inline rx_ret_t _uart_trx_rx(struct _transceiver *ifdev, void* buf, size_t size, size_t *len, long timeout, long interval, const char *okFlag, rx_flag_t okFlagType, const char *failFlag, rx_flag_t failFlagType) {
	if(!ifdev || !ifdev->hif) return STD_ERR_INVALID_STATE;
	return uart_read((std_uart_t*)ifdev->hif, (unsigned char*)buf, size, len, timeout, interval, okFlag, okFlagType, failFlag, failFlagType);
}

static inline std_err_t _uart_trx_rxline(struct _transceiver *ifdev, void* buf, size_t size, size_t *len) {
	if(!ifdev || !ifdev->hif) return STD_ERR_INVALID_STATE;
	return uart_readline((std_uart_t*)ifdev->hif, (unsigned char*)buf, size, len);
}

static inline std_err_t _uart_trx_tx(struct _transceiver *ifdev, const void* buf, size_t size, size_t *len) {
	if(!ifdev || !ifdev->hif) return STD_ERR_INVALID_STATE;
	return uart_write((std_uart_t*)ifdev->hif, (const unsigned char*)buf, size, len);
}

static inline void _uart_trx_close(struct _transceiver *ifdev) {
	//if(!ifdev || !ifdev->hif) return;
}

static inline size_t _uart_trx_get_mtu(struct _transceiver *ifdev) {
	return 1024;
}

static inline std_err_t _uart_trx_clear_rx_buffer(struct _transceiver *ifdev) {
	return STD_ERR_OK;
}

#ifdef XPAR_XUARTPS_0_DEVICE_ID
static transceiver_t _transceiver_uart0 = {&_ps_uart0, _uart_trx_get_mtu, _uart_trx_clear_rx_buffer, _uart_trx_rx, _uart_trx_rxline, _uart_trx_tx, _uart_trx_close, NULL, NULL};
transceiver_t* const transceiver_uart0 = &_transceiver_uart0;
#endif

#ifdef XPAR_XUARTPS_1_DEVICE_ID
static transceiver_t _transceiver_uart1 = {&_ps_uart1, _uart_trx_get_mtu, _uart_trx_clear_rx_buffer, _uart_trx_rx, _uart_trx_rxline, _uart_trx_tx, _uart_trx_close, NULL, NULL};
transceiver_t* const transceiver_uart1 = &_transceiver_uart1;
#endif

transceiver_t* transceiver_open_uart(const char* addr) {
	int port, baudrate=115200, databits=8, stopbits=1;
	char parity='n';

	int narg = sscanf(addr, "uart://%d?baudrate=%d&databits=%d&stopbits=%d&parity=%c", &port, &baudrate, &databits, &stopbits, &parity);
	if(narg < 1) return NULL;

	transceiver_t* trx = NULL;
	if(port == 0) {
#ifdef XPAR_XUARTPS_0_DEVICE_ID
		trx = transceiver_uart0;
#else
		return NULL;
#endif
	} else if(port == 1) {
#ifdef XPAR_XUARTPS_1_DEVICE_ID
		trx = transceiver_uart1;
#else
		return NULL;
#endif
	} else {
		return NULL;
	}

	std_err_t err = uart_init(trx->hif, baudrate, NULL, NULL);
	if(err) return NULL;

	return trx;
}
